/*
 * Copyright 2012-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.web.servlet.context;

import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.context.support.XmlWebApplicationContext;

/**
 * 继承自 {@link ServletWebServerApplicationContext}，通过 XML 文档进行配置，
 * 这些 XML 文档由 {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader} 解析。
 * <p>
 * 注意：若指定多个配置位置，后加载的 Bean 定义会覆盖先加载的定义，
 * 这可以用来通过额外的 XML 文件有选择地覆盖某些 Bean 定义。
 *
 * @author Phillip Webb
 * @since 1.0.0
 * @see #setNamespace
 * @see #setConfigLocations
 * @see ServletWebServerApplicationContext
 * @see XmlWebApplicationContext
 */
public class XmlServletWebServerApplicationContext extends ServletWebServerApplicationContext {

	private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);

	/**
	 * 创建一个新的 {@link XmlServletWebServerApplicationContext}，需要调用
	 * {@linkplain #load} 加载配置，然后手动调用 {@link #refresh} 刷新上下文。
	 */
	public XmlServletWebServerApplicationContext() {
		this.reader.setEnvironment(getEnvironment());
	}

	/**
	 * 创建一个新的 {@link XmlServletWebServerApplicationContext}，从给定的资源加载 Bean 定义，
	 * 并自动刷新上下文。
	 * @param resources 资源数组
	 */
	public XmlServletWebServerApplicationContext(Resource... resources) {
		load(resources);
		refresh();
	}

	/**
	 * 创建一个新的 {@link XmlServletWebServerApplicationContext}，从给定的资源位置加载 Bean 定义，
	 * 并自动刷新上下文。
	 * @param resourceLocations 资源位置数组
	 */
	public XmlServletWebServerApplicationContext(String... resourceLocations) {
		load(resourceLocations);
		refresh();
	}

	/**
	 * 创建一个新的 {@link XmlServletWebServerApplicationContext}，从相对于指定类包路径的资源位置加载 Bean 定义，
	 * 并自动刷新上下文。
	 * @param relativeClass 作为资源路径前缀的类
	 * @param resourceNames 资源名称数组
	 */
	public XmlServletWebServerApplicationContext(Class<?> relativeClass, String... resourceNames) {
		load(relativeClass, resourceNames);
		refresh();
	}

	/**
	 * 设置是否启用 XML 验证，默认值为 {@code true}。
	 * @param validating 是否启用验证
	 */
	public void setValidating(boolean validating) {
		this.reader.setValidating(validating);
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * 将指定的环境对象传递给底层的 {@link XmlBeanDefinitionReader}。
	 * 该方法应在任何 {@link #load} 调用之前执行。
	 */
	@Override
	public void setEnvironment(ConfigurableEnvironment environment) {
		super.setEnvironment(environment);
		this.reader.setEnvironment(getEnvironment());
	}

	/**
	 * 从给定的 XML 资源加载 Bean 定义。
	 * @param resources 一个或多个要加载的资源
	 */
	public final void load(Resource... resources) {
		this.reader.loadBeanDefinitions(resources);
	}

	/**
	 * 从给定的 XML 资源位置加载 Bean 定义。
	 * @param resourceLocations 一个或多个资源位置
	 */
	public final void load(String... resourceLocations) {
		this.reader.loadBeanDefinitions(resourceLocations);
	}

	/**
	 * 从给定类的包路径为前缀的相对资源路径加载 Bean 定义。
	 * @param relativeClass 资源相对的类，其包路径将作为资源名前缀
	 * @param resourceNames 相对资源名数组
	 */
	public final void load(Class<?> relativeClass, String... resourceNames) {
		Resource[] resources = new Resource[resourceNames.length];
		for (int i = 0; i < resourceNames.length; i++) {
			resources[i] = new ClassPathResource(resourceNames[i], relativeClass);
		}
		this.reader.loadBeanDefinitions(resources);
	}

}
